﻿using System;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommandLine.OptParse;
using CommandLine.ConsoleUtils;
using TestListGenerator.Common;
namespace TestListGenerator
{
    /// <summary>
    /// The wdonderful little TestListGenerator program. For more 
    /// details, see http://www.codeplex.com/testlistgenerator
    /// </summary>
    public class Program
    {
        /// <summary>
        /// The parser used to parse command line arguments
        /// </summary>
        static Parser commandLineArgumentParser;

        /// <summary>
        /// This method exist the program with a status code. It
        /// allows you to see the mesage if you are in debug mode
        /// </summary>
        /// <param name="statusCode">The status code of the error</param>
        static void Exit(int statusCode)
        {
            // Give the debugger a chance :)
            if (System.Diagnostics.Debugger.IsAttached)
            {
                Console.WriteLine("Press a key to quit...");
                Console.ReadKey(true);
            }
            System.Environment.Exit(statusCode);
        }

        /// <summary>
        /// The help screenis shown if the user didn't parse in the required options
        /// </summary>
        static void ShowUsage()
        {
            UsageBuilder usage = new UsageBuilder();
            usage.GroupOptionsByCategory = true;

            usage.BeginSection("Name");
            usage.AddParagraph("tlg.exe ["+Assembly.GetExecutingAssembly().GetName(false).Version+"]- Test List Generator for Visual Studio");
            usage.EndSection();

            usage.BeginSection("Author");
            usage.AddParagraph("Shaun McCarthy (http://www.shaunmccarthy.com/about)");
            usage.EndSection();

            usage.BeginSection("Description");
            usage.AddParagraph(
@"Program used to generate TestList files for use with mstest. It generates 
three TestList: All, Included and Excluded.

    * All runs all tests 
    * Included runs all tests that match the criteria
    * Excluded runs all the tests that didn't match the criteria
");
            usage.EndSection();

            usage.BeginSection("Criteria");
            usage.AddParagraph(@"
Use either the [TestProperty(""Category"",""CategoryName"")] attributes or 
the [TestCategory(""CategoryName""] attributes (defined in 
TestListGenerator.Attributes) to assign each of your Test cases in the 
target assembly a Category. Then use the i parameter to define each 
category you want to include in this test run. Use the e parameter
to define which tests to exclude
");
            usage.EndSection();

            // Generate the list of arguments and descriptions automagically
            usage.BeginSection("Arguments");
            usage.AddOptions(commandLineArgumentParser);
            usage.EndSection();

            usage.BeginSection("Examples");
            usage.AddParagraph(
@"Here are some examples:

1. Only include long running tests
tlg.exe -d MyProgram.Tests.dll -i LongRunningTests -o output.vsmdi

2. Only include long running tests that don't call webservices
tlg.exe -d MyProgram.Tests.dll -i LongRunningTests -e WebServiceTests -o output.vsmdi

3. Exclude all long running tests
tlg.exe -d MyProgram.Tests.dll -e LongRunning -o output.vsdmi

4. Include multiple types of tests, but not LongRunningTests
tlg.exe -d MyProgram.Tests.dll -i TestType1 -i TestType2 -e LongRunningTests -o output.vsmdi
");
            usage.EndSection();

            usage.ToText(Console.Out, OptStyle.Unix, true);
        }

        /// <summary>
        /// Parse the aguments using the CSharpOptPartse library
        /// </summary>
        /// <param name="args">The arguments passed in to the application</param>
        public static Configuration ParseArguments(string[] args)
        {
            Configuration config = new Configuration();
            commandLineArgumentParser = ParserFactory.BuildParser(config);
            commandLineArgumentParser.OptStyle = OptStyle.Unix;
            commandLineArgumentParser.UnixShortOption = UnixShortOption.CollapseShort;
            commandLineArgumentParser.UnknownOptHandleType = UnknownOptHandleType.Error;
            commandLineArgumentParser.DupOptHandleType = DupOptHandleType.Allow;
            commandLineArgumentParser.CaseSensitive = false;
            commandLineArgumentParser.SearchEnvironment = false;

            // Parse the args
            commandLineArgumentParser.Parse(args);

            return config;
        }



        /// <summary>
        /// Parse the arguments, check the category rules and then
        /// create the test list file
        /// </summary>
        /// <param name="args">Arguments passed into the application</param>
        static void Main(string[] args)
        {
            // Parse the parameters
            Configuration config = ParseArguments(args);

            // Make sure we got the right parameters
            try
            {
                Console.WriteLine("[Test List Generator v" + Assembly.GetExecutingAssembly().GetName(false).Version + "]");

                // Create the generator
                TestListGenerator tlg = new TestListGenerator(config);

                // Run it
                tlg.GenerateTestListBasedOnCategories();

                // Give the user some feedback
                Console.WriteLine("VSMDI Config file written out to " + tlg.Configuration.OutputPath);
                Console.WriteLine(@"
How to use this config file (Included Tests Only)
mstest.exe " + tlg.MSTestParameters(true) + @"

How to use this config file (Excluded Tests Only)
mstest.exe " + tlg.MSTestParameters(false) + @"
");
            }
            catch (TestListGeneratorException ex)
            {
                if (ex.Type == TestListGeneratorExceptionType.MissingConfigurationParameter)
                {
                    Console.WriteLine("[ERROR] Unable to generate test list, due to: " + ex.Message);
                    ShowUsage();
                }
                if (ex.Type == TestListGeneratorExceptionType.TestDLLInvalidFormat || ex.Type == TestListGeneratorExceptionType.TestDLLMissing)
                {
                    Console.WriteLine("[ERROR] Unable to generate test list, due to: " + ex.Message);
                }
            }
            catch (Exception ex)
            {
                // Unknown Error
                Console.WriteLine("[ERROR] An unknown error was encountered while generating your test list, please post to discussion list at http://www.codeplex.com/testlistgenerator");
                Console.WriteLine(Dumper.Dump(ex));
                Exit(-2);
            }

            // In debug mode, prompt the user to press a key before closing
            if (System.Diagnostics.Debugger.IsAttached)
            {
                Console.WriteLine("Press a key to quit...");
                Console.ReadKey(true);
            }
        }
    }
}
